<template>
  <Drawer
    title="用例断言 | 提取变量"
    :closable="false"
    :mask-closable="false"
    :transfer="false"
    width="100"
    v-model="showExpectResultEditDrawer"
  >
    <Card title="用例断言" :bordered="false" :dis-hover="true">
      <div slot="extra">
        <Button type="primary" @click="addValidate">添加</Button>
      </div>
      <div>
        <Table
          :columns="validateColumns"
          :data="validateData"
          border
          size="small"
        >
          <template slot-scope="{ row, index }" slot="validateType">
            <Select
              transfer
              v-model="validateData[index].validateType"
              @on-change="changeValidateType(index)"
            >
              <Option
                v-for="(item, index) in types"
                :value="item.value"
                :label="item.name"
                :key="index"
              >
                <span>{{ item.name }}</span>
                <span style="float: right">{{ item.value }}</span>
              </Option>
            </Select>
          </template>
          <template slot-scope="{ row, index }" slot="validateField">
            <Input
              v-model="validateData[index].validateField"
              :clearable="
                ['headers', 'cookies', 'results'].includes(
                  validateData[index].validateType
                )
              "
              :readonly="
                ['url', 'status_code', 'elapsed'].includes(
                  validateData[index].validateType
                )
              "
            ></Input>
          </template>
          <template slot-scope="{ row, index }" slot="validateMethod">
            <Select transfer v-model="validateData[index].validateMethod">
              <Option
                v-for="(item, index) in validateMethods"
                :value="item.value"
                :label="item.name"
                :key="index"
              >
                <span>{{ item.value }}</span>
                <span style="float: right; color: #ccc">{{ item.name }}</span>
              </Option>
            </Select>
          </template>
          <template slot-scope="{ row, index }" slot="expectResultType">
            <Select transfer v-model="validateData[index].expectResultType">
              <Option
                v-for="(item, index) in expectResultTypes"
                :value="item.value"
                :label="item.name"
                :key="index"
              >
                <span>{{ item.name }}</span>
                <span style="float: right; color: #ccc">{{ item.value }}</span>
              </Option>
            </Select>
          </template>
          <template slot-scope="{ row, index }" slot="expectResult">
            <Input
              v-model="validateData[index].expectResult"
              clearable
              :number="validateData[index].validateType === 'status_code'"
            ></Input>
          </template>
          <template slot-scope="{ row, index }" slot="action">
            <Button type="error" size="small" @click="removeValidate(index)"
              >删除</Button
            >
          </template>
        </Table>
      </div>
    </Card>
    <Card title="提取变量" :bordered="false" :dis-hover="true">
      <div slot="extra" class="card-extra">
        <Button type="primary" class="card-extra-item" @click="addOutput"
          >添加</Button
        >
      </div>
      <div>
        <Table :columns="outputColumns" :data="outputData" border size="small">
          <template slot-scope="{ row, index }" slot="outputType">
            <Select
              transfer
              v-model="outputData[index].outputType"
              @on-change="changeOutputType(index)"
            >
              <Option
                v-for="(item, index) in types"
                :value="item.value"
                :label="item.name"
                :key="index"
              >
                <span>{{ item.name }}</span>
                <span style="float: right; color: #ccc">{{ item.value }}</span>
              </Option>
            </Select>
          </template>
          <template slot-scope="{ row, index }" slot="outputName">
            <Input v-model="outputData[index].outputName" clearable></Input>
          </template>
          <template slot-scope="{ row, index }" slot="outputValue">
            <Input
              v-model="outputData[index].outputValue"
              :clearable="
                ['headers', 'cookies', 'results'].includes(
                  outputData[index].outputType
                )
              "
              :readonly="
                ['url', 'status_code', 'elapsed'].includes(
                  outputData[index].outputType
                )
              "
            ></Input>
          </template>
          <template slot-scope="{ row, index }" slot="action">
            <Button type="error" size="small" @click="removeOutput(index)"
              >删除</Button
            >
          </template>
        </Table>
      </div>
    </Card>
    <div class="drawer-footer">
      <Button class="drawer-footer-item" @click="handleCreateExpectResultCancel"
        >取消</Button
      >
      <Button
        type="primary"
        class="drawer-footer-item"
        @click="handleCreateExpectResultSubmit"
        >确定</Button
      >
    </div>
  </Drawer>
</template>
<script>
export default {
  name: "expect-result-edit",
  components: {},
  props: {
    showDrawer: {
      type: Boolean,
      default: false,
    },
    expectResult: {
      type: Object,
      default: () => {
        return {};
      },
    },
  },
  watch: {
    expectResult(val) {
      if (this.showExpectResultEditDrawer && val) {
        let output = val.output;
        let validate = val.validate;
        let tempOutput = [];
        let tempValidate = [];
        if (validate) {
          if (Array.isArray(validate)) {
            tempValidate = validate.map((item) => {
              let validateType = "results";
              if (["url", "status_code", "elapsed"].includes(item.check)) {
                validateType = item.check;
              } else if (item.check.indexOf("cookies") == 1) {
                validateType = "cookies";
              } else if (item.check.indexOf("headers") == 1) {
                validateType = "headers";
              }
              let expectResult = item.expect;
              let expectResultType = typeof item.expect;
              if (expectResultType == "object") {
                expectResult = JSON.stringify(expectResult);
              }
              return {
                validateType: validateType,
                validateField: item.check,
                validateMethod: item.comparator,
                expectResult: expectResult,
                expectResultType: expectResultType,
              };
            });
          } else {
            let validateType = "results";
            if (["url", "status_code", "elapsed"].includes(validate.check)) {
              validateType = validate.check;
            } else if (validate.check.indexOf("cookies") == 1) {
              validateType = "cookies";
            } else if (validate.check.indexOf("headers") == 1) {
              validateType = "headers";
            }
            let expectResult = validate.expect;
            let expectResultType = typeof validate.expect;
            if (expectResultType == "object") {
              expectResult = JSON.stringify(expectResult);
            }
            tempValidate = [
              {
                validateType: validateType,
                validateField: validate.check,
                validateMethod: validate.comparator,
                expectResult: expectResult,
                expectResultType: expectResultType,
              },
            ];
          }
        }
        if (output) {
          if (Array.isArray(output)) {
            tempOutput = output.map((item) => {
              let outputType = "results";
              let name = Object.keys(item)[0];
              let value = item[name];
              if (["url", "status_code", "elapsed"].includes(value)) {
                outputType = value;
              } else if (value.indexOf("cookies") == 1) {
                outputType = "cookies";
              } else if (value.indexOf("headers") == 1) {
                outputType = "headers";
              }
              return {
                outputType: outputType,
                outputName: name,
                outputValue: value,
              };
            });
          } else {
            let outputType = "results";
            let name = Object.keys(output)[0];
            let value = output[name];
            if (["url", "status_code", "elapsed"].includes(value)) {
              outputType = value;
            } else if (value.indexOf("cookies") == 1) {
              outputType = "cookies";
            } else if (value.indexOf("headers") == 1) {
              outputType = "headers";
            }
            tempOutput = [
              {
                outputType: outputType,
                outputName: name,
                outputValue: value,
              },
            ];
          }
        }
        this.validateData = tempValidate;
        this.outputData = tempOutput;
      }
    },
  },
  computed: {
    showExpectResultEditDrawer: {
      get() {
        return this.showDrawer;
      },
      set(val) {
        this.$emit("update:showDrawer", val);
      },
    },
  },
  data() {
    return {
      types: [
        {
          name: "请求URL",
          value: "url",
        },
        {
          name: "请求头",
          value: "headers",
        },
        {
          name: "COOKIES",
          value: "cookies",
        },
        {
          name: "响应状态码",
          value: "status_code",
        },
        {
          name: "请求响应内容",
          value: "results",
        },
        {
          name: "请求响应时长",
          value: "elapsed",
        },
      ],
      validateMethods: [
        { value: "eq", name: "实际值与期望值相等" },
        { value: "lt", name: "实际值小于期望值" },
        { value: "lte", name: "实际值小于或等于期望值" },
        { value: "gt", name: "实际值大于期望值" },
        { value: "gte", name: "实际值大于或等于期望值" },
        { value: "neq", name: "实际值与期望值不相等" },
        { value: "str_eq", name: "字符串实际值与期望值相同" },
        { value: "length_eq", name: "实际值的长度等于期望长度" },
        { value: "length_gt", name: "实际值的长度大于期望长度" },
        {
          value: "length_gte",
          name: "实际值的长度大于或等于期望长度",
        },
        { value: "length_lt", name: "实际值的长度小于期望长度" },
        {
          value: "length_lte",
          name: "实际值的长度小于或等于期望长度",
        },
        { value: "contains", name: "期望值包含在实际值中" },
        { value: "contained_by", name: "实际值被包含在期望值中" },
        {
          value: "type_match",
          name: "实际值的类型与期望值的类型相匹配",
        },
        {
          value: "regex_match",
          name: "正则匹配(从字符串的起始位置匹配)",
        },
        {
          value: "regex_search",
          name: "正则匹配(从字符串的任意位置匹配)",
        },
        { value: "startswith", name: "实际值是以期望值开始" },
        { value: "endswith", name: "实际值是以期望值结束" },
      ],
      expectResultTypes: [
        {
          name: "数值",
          value: "number",
        },
        {
          name: "字符串",
          value: "string",
        },
        {
          name: "布尔值",
          value: "boolean",
        },
        {
          name: "对象",
          value: "object",
        },
        {
          name: "空",
          value: "null",
        },
        {
          name: "未定义",
          value: "undefined",
        },
      ],
      validateColumns: [
        {
          title: "断言类型（必填）",
          slot: "validateType",
          align: "center",
          width: 250,
        },
        {
          title: "断言字段（JSONPATH表达式）（必填）",
          slot: "validateField",
          align: "center",
        },
        {
          title: "断言方法（必填）",
          slot: "validateMethod",
          align: "center",
        },
        {
          title: "期望结果类型",
          slot: "expectResultType",
          align: "center",
          width: 200,
        },
        {
          title: "期望结果",
          slot: "expectResult",
          align: "center",
        },
        {
          title: "操作",
          slot: "action",
          align: "center",
          width: 100,
        },
      ],
      validateData: [],
      outputColumns: [
        {
          title: "提取源类型（必填）",
          slot: "outputType",
          align: "center",
          width: 250,
        },
        {
          title: "存储变量名（必填）",
          slot: "outputName",
          align: "center",
          width: 300,
        },
        {
          title: "提取表达式（JSONPATH表达式）（必填）",
          slot: "outputValue",
          align: "center",
        },
        {
          title: "操作",
          slot: "action",
          align: "center",
          width: 100,
        },
      ],
      outputData: [],
    };
  },
  methods: {
    addValidate() {
      let data = {
        validateType: "results",
        validateField: "$.results.",
        validateMethod: "eq",
        expectResultType: "string",
        expectResult: "",
      };
      this.validateData.push(data);
    },
    addOutput() {
      let data = {
        outputType: "results",
        outputName: "",
        outputValue: "$.results.",
      };
      this.outputData.push(data);
    },
    changeValidateType(index) {
      let validateType = this.validateData[index].validateType;
      if (
        ["url", "headers", "status_code", "elapsed", "cookies"].includes(
          validateType
        )
      ) {
        this.validateData[index].validateField = validateType;
      } else if (validateType == "results") {
        this.validateData[index].validateField = "$.results.";
      } else {
        this.validateData[index].validateField = "";
      }
    },
    changeOutputType(index) {
      let outputType = this.outputData[index].outputType;
      if (
        ["url", "headers", "status_code", "elapsed", "cookies"].includes(
          outputType
        )
      ) {
        this.outputData[index].outputValue = outputType;
      } else if (outputType == "results") {
        this.outputData[index].outputValue = "$.results.";
      } else {
        this.outputData[index].outputValue = "";
      }
    },
    removeOutput(index) {
      this.outputData.splice(index, 1);
    },
    removeValidate(index) {
      this.validateData.splice(index, 1);
    },
    handleCreateExpectResultCancel() {
      this.showExpectResultEditDrawer = false;
      this.$emit("update:expectResult", {});
    },
    handleCreateExpectResultSubmit() {
      let validate = this.validateData.map((item) => {
        if (item.validateField == "") {
          this.$Message.warning("断言字段 不能为空");
          throw Error("终止循环");
        }

        let expectResult = item.expectResult;
        let expectResultType = item.expectResultType;

        if (expectResultType == "number") {
          expectResult = Number(expectResult);
        }
        if (
          expectResultType == "boolean" &&
          [true, false, "true", "false"].includes(expectResult)
        ) {
          expectResult = JSON.parse(expectResult);
        }
        if (
          ["undefined", "null"].includes(expectResultType) ||
          (expectResultType == "object" && expectResult == "null")
        ) {
          expectResult = null;
        }
        if (expectResultType == "object" && typeof expectResult !== "object") {
          expectResult = JSON.parse(expectResult);
        }
        if (expectResultType == "string" && typeof expectResult !== "string") {
          expectResult = JSON.stringify(expectResult);
        }

        return {
          check: item.validateField,
          expect: expectResult,
          comparator: item.validateMethod,
        };
      });
      let output = this.outputData.map((item) => {
        if (item.outputName == "") {
          this.$Message.warning("存储变量名 不能为空");
          throw Error("终止循环");
        }
        let name = item.outputName;
        let value = item.outputValue;
        return {
          [name]: value,
        };
      });
      let expectResult = {
        output: output,
        validate: validate,
      };
      this.$emit("updateExpectResult", expectResult);
      this.handleCreateExpectResultCancel();
    },
  },
};
</script>
<style lang="less" scoped>
.card-extra {
  display: flex;
  .card-extra-item {
    margin-left: 10px;
  }
}
.drawer-footer {
  width: 100%;
  position: absolute;
  bottom: 0;
  left: 0;
  border-top: 1px solid #e8e8e8;
  padding: 10px 16px;
  text-align: right;
  background: #fff;
  .drawer-footer-item {
    margin: 0 0 0 10px;
  }
}
</style>